#define _CRT_SECURE_NO_WARNINGS  // 禁用编译器对不安全函数的警告

#define HAVE_REMOTE  // 指定我们在使用 WinPcap 的远程捕获功能
#define WIN32  // 定义平台为 Windows
#define WPCAP  // 使用 WinPcap 库
#pragma warning(default : 4005)  // 恢复默认的编译警告
#pragma comment(lib, "wpcap.lib")  // 链接 wpcap 库
#pragma comment(lib, "Packet.lib")  // 链接 Packet 库
#pragma comment(lib, "ws2_32.lib")  // 链接 Winsock2 库

#include <stdio.h>  // 标准输入输出库
#include <time.h>   // 时间库
#include <map>      // C++ 标准库的 map 容器
#include <chrono>   // C++11 定时器库
#include <thread>   // C++11 线程库
#include <mutex>    // C++11 互斥锁
#include <pcap.h>   // WinPcap 库，用于捕获网络数据包

// 定义以太网帧头结构
struct eth_header {
    u_char dest_mac[6];  // 目标 MAC 地址
    u_char src_mac[6];   // 源 MAC 地址
    u_short eth_type;    // 以太网类型（用于识别协议，如 IPv4、IPv6 等）
};

// 定义 IP 报文头结构
struct ip_header {
    u_char  ver_ihl;        // 版本（4 bits）+ IP 报头长度（4 bits）
    u_char  tos;            // 服务类型
    u_short tlen;           // 总长度
    u_short identification; // 标识符
    u_short flags_fo;       // 标志位（3 bits）+ 分片偏移（13 bits）
    u_char  ttl;            // 生存时间
    u_char  proto;          // 协议类型
    u_short crc;            // 校验和
    u_char  saddr[4];       // 源 IP 地址
    u_char  daddr[4];       // 目的 IP 地址
    u_int   op_pad;         // 可选项 + 填充
};

// 统计 MAC 地址和 IP 地址流量的 map 结构，分别用于存储从源和目标地址的流量
std::map<std::string, unsigned long long> traffic_from_mac, traffic_to_mac, traffic_from_ip, traffic_to_ip;
std::mutex stats_mutex;  // 互斥锁，保护统计信息的读写
bool continue_capture = true;  // 控制是否继续捕获数据包

// 打印流量统计信息
void print_stats() {
    while (continue_capture) {
        std::this_thread::sleep_for(std::chrono::minutes(1));  // 每隔 1 分钟打印一次
        stats_mutex.lock();  // 加锁以保护共享数据
        // 打印源 MAC 地址的流量统计
        printf("Traffic From MAC Addresses:\n");
        for (auto& entry : traffic_from_mac) {
            printf("MAC: %s, Data: %llu bytes\n", entry.first.c_str(), entry.second);
        }
        // 打印目标 MAC 地址的流量统计
        printf("Traffic To MAC Addresses:\n");
        for (auto& entry : traffic_to_mac) {
            printf("MAC: %s, Data: %llu bytes\n", entry.first.c_str(), entry.second);
        }
        // 打印源 IP 地址的流量统计
        printf("Traffic From IP Addresses:\n");
        for (auto& entry : traffic_from_ip) {
            printf("IP: %s, Data: %llu bytes\n", entry.first.c_str(), entry.second);
        }
        // 打印目标 IP 地址的流量统计
        printf("Traffic To IP Addresses:\n");
        for (auto& entry : traffic_to_ip) {
            printf("IP: %s, Data: %llu bytes\n", entry.first.c_str(), entry.second);
        }
        // 重置流量统计
        traffic_from_mac.clear();
        traffic_to_mac.clear();
        traffic_from_ip.clear();
        traffic_to_ip.clear();
        stats_mutex.unlock();  // 解锁
    }
}

// 数据包处理函数，用于处理捕获到的每一个网络数据包
void packet_handler(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
    struct eth_header* eth = (struct eth_header*)packet;  // 解析以太网帧头
    // 如果是 IPv4 报文（以太网类型为 0x0800）
    if (ntohs(eth->eth_type) == 0x0800) {  // 转换网络字节序为主机字节序
        struct ip_header* ip = (struct ip_header*)(packet + 14);  // 跳过以太网帧头（14字节）解析 IP 报头

        FILE* log_file = (FILE*)user;  // 获取日志文件指针
        char src_mac[18], dest_mac[18], src_ip[16], dest_ip[16];  // 用于存储格式化后的地址

        // 将源 MAC 地址格式化为字符串
        sprintf(src_mac, "%02x-%02x-%02x-%02x-%02x-%02x",
            eth->src_mac[0], eth->src_mac[1], eth->src_mac[2], eth->src_mac[3], eth->src_mac[4], eth->src_mac[5]);
        // 将目标 MAC 地址格式化为字符串
        sprintf(dest_mac, "%02x-%02x-%02x-%02x-%02x-%02x",
            eth->dest_mac[0], eth->dest_mac[1], eth->dest_mac[2], eth->dest_mac[3], eth->dest_mac[4], eth->dest_mac[5]);
        // 将源 IP 地址格式化为字符串
        sprintf(src_ip, "%d.%d.%d.%d", ip->saddr[0], ip->saddr[1], ip->saddr[2], ip->saddr[3]);
        // 将目标 IP 地址格式化为字符串
        sprintf(dest_ip, "%d.%d.%d.%d", ip->daddr[0], ip->daddr[1], ip->daddr[2], ip->daddr[3]);

        time_t now = time(NULL);  // 获取当前时间
        struct tm* now_tm = localtime(&now);  // 将时间转换为本地时间结构

        // 将数据包信息写入日志文件
        fprintf(log_file, "%d-%02d-%02d %02d:%02d:%02d,%s,%s,%s,%s,%d\n",
            now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday,
            now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec,
            src_mac, src_ip, dest_mac, dest_ip, pkthdr->len);

        // 在控制台打印数据包信息
        printf("%d-%02d-%02d %02d:%02d:%02d, %s, %s, %s, %s, %d\n",
            now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday,
            now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec,
            src_mac, src_ip, dest_mac, dest_ip, pkthdr->len);

        // 更新流量统计
        stats_mutex.lock();  // 加锁保护数据
        traffic_from_mac[src_mac] += pkthdr->len;  // 更新源 MAC 地址流量
        traffic_to_mac[dest_mac] += pkthdr->len;  // 更新目标 MAC 地址流量
        traffic_from_ip[src_ip] += pkthdr->len;  // 更新源 IP 地址流量
        traffic_to_ip[dest_ip] += pkthdr->len;  // 更新目标 IP 地址流量
        stats_mutex.unlock();  // 解锁
    }
}

int main() {
    pcap_if_t* alldevs, * device;  // 网络设备列表
    int i = 0, inum;  // 设备计数器和选择的设备编号
    char errbuf[PCAP_ERRBUF_SIZE];  // 错误缓冲区
    pcap_t* handle;  // 用于捕获数据包的句柄

    // 获取系统中的网络设备列表
    if (pcap_findalldevs(&alldevs, errbuf) == -1) {
        fprintf(stderr, "Error finding devices: %s\n", errbuf);  // 如果失败，输出错误信息
        return -1;
    }

    // 打印可用网络设备列表
    for (device = alldevs; device; device = device->next) {
        printf("%d. %s - %s\n", ++i, device->name, device->description ? device->description : "No description available");
    }

    if (i == 0) {
        printf("\nNo interfaces found! Make sure WinPcap is installed.\n");  // 如果没有找到设备，提示用户
        return -1;
    }

    printf("Enter the interface number (1-%d): ", i);  // 提示用户选择一个设备编号
    scanf("%d", &inum);  // 读取用户输入的设备编号

    if (inum < 1 || inum > i) {
        printf("\nAdapter number out of range.\n");  // 如果编号不在范围内，输出错误信息
        pcap_freealldevs(alldevs);  // 释放设备列表
        return -1;
    }

    // 跳到选择的设备
    for (device = alldevs, i = 0; i < inum - 1; device = device->next, i++);

    // 打开选定的网络设备
    handle = pcap_open_live(device->name, 65536, 1, 1000, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "Unable to open the adapter. %s is not supported by WinPcap\n", device->name);  // 如果打开失败，输出错误信息
        pcap_freealldevs(alldevs);  // 释放设备列表
        return -1;
    }

    printf("Listening on %s...\n", device->description);  // 显示监听的设备信息

    // 打开日志文件用于记录捕获的数据
    FILE* log_file = fopen("packets_log.csv", "w");
    if (log_file == NULL) {
        fprintf(stderr, "Could not open log file\n");  // 如果打开日志文件失败，输出错误信息
        return -1;
    }

    // 写入日志文件的 CSV 表头
    fprintf(log_file, "Time,Source MAC,Source IP,Destination MAC,Destination IP,Frame Length\n");

    // 启动打印统计信息的线程
    std::thread stats_thread(print_stats);
    pcap_loop(handle, 0, packet_handler, (u_char*)log_file);  // 开始捕获数据包并调用处理函数
    continue_capture = false;  // 停止捕获标志
    stats_thread.join();  // 等待统计线程结束

    fclose(log_file);  // 关闭日志文件
    pcap_close(handle);  // 关闭捕获句柄
    pcap_freealldevs(alldevs);  // 释放设备列表

    return 0;
}

